`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: VD
// Engineer: dwliu
// 
// Create Date:    02:15:50 08/17/2010 
// Design Name: 
// Module Name:    icap_ctrl.v
// Project Name: 
// Target Devices: 
// Tool versions: ISE11.3
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module icap_ctrl(
	input	wire		clk,
	input	wire		rstn,
	
	input	wire	[23:0]	start_addr,
	input	wire		addr_preload,		//1: multi-boot address will preload during generating bit file
	input	wire		start,
	output	wire		busy
	);

parameter	SYNC_H		= 8'b1010_1010,			//{D0,D1,D2...D7}
		SYNC_L		= 8'b1001_1001,
		W_GENERAL1_H	= 8'b0011_0010,
		W_GENERAL1_L	= 8'b0110_0001,
		W_GENERAL2_H	= 8'b0011_0010,
		W_GENERAL2_L	= 8'b1000_0001,
		W_CMD_H		= 8'b0011_0000,
		W_CMD_L		= 8'b1010_0001,
		CMD_REBOOT_H	= 8'b0000_0000,
		CMD_REBOOT_L	= 8'b0000_1110,
		NOP_H		= 8'b0010_0000,
		NOP_L		= 8'b0000_0000;

parameter	IDLE		= 5'd0,
		SYNC1		= 5'd1,
		SYNC2		= 5'd2,
		W_GEN1_1	= 5'd3,
		W_GEN1_2	= 5'd4,
		W_LADDR_1	= 5'd5,
		W_LADDR_2	= 5'd6,
		W_GEN2_1	= 5'd7,
		W_GEN2_2	= 5'd8,
		W_HADDR_1	= 5'd9,
		W_HADDR_2	= 5'd10,
		W_CMD1		= 5'd11,
		W_CMD2		= 5'd12,
		CMD_REBOOT1	= 5'd13,
		CMD_REBOOT2	= 5'd14,
		NOP1		= 5'd15,
		NOP2		= 5'd16;

reg	[4:0]	state;
reg		icap_write,icap_ce;
reg	[7:0]	icap_i;
wire	[7:0]	icap_i_reverse;


//FSM
always	@(posedge clk)
begin
	if(!rstn)
		state<=IDLE;
	else
	begin
		case(state)
		IDLE:
		begin
			if(start==1'b1)		//begin to multi-boot
				state<=SYNC1;
			else
				state<=IDLE;
		end
		SYNC1:
			state<=SYNC2;
		SYNC2:
		begin
			if(addr_preload==1'b0)		//should input target address
				state<=W_GEN1_1;
			else
				state<=W_CMD1;
		end
		W_GEN1_1:
			state<=W_GEN1_2;
		W_GEN1_2:
			state<=W_LADDR_1;
		W_LADDR_1:
			state<=W_LADDR_2;
		W_LADDR_2:
			state<=W_GEN2_1;
		W_GEN2_1:
			state<=W_GEN2_2;
		W_GEN2_2:
			state<=W_HADDR_1;
		W_HADDR_1:
			state<=W_HADDR_2;
		W_HADDR_2:
			state<=W_CMD1;
		W_CMD1:
			state<=W_CMD2;
		W_CMD2:
			state<=CMD_REBOOT1;
		CMD_REBOOT1:
			state<=CMD_REBOOT2;
		CMD_REBOOT2:
			state<=NOP1;
		NOP1:
			state<=NOP2;
		NOP2:
			state<=IDLE;
		default:
			state<=IDLE;
		endcase
	end
end			

always	@(posedge clk)
begin
	if(!rstn)
	begin
		icap_ce<=1'b1;			//active low
		icap_i<=8'hff;
		icap_write<=1'b1;		//active low
	end
	else
	begin
		case(state)
		IDLE:
		begin
			icap_ce<=1'b1;
			icap_write<=1'b1;
			icap_i<=8'hff;
		end
		SYNC1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=SYNC_H;
		end
		SYNC2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=SYNC_L;
		end
		W_GEN1_1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=W_GENERAL1_H;
		end
		W_GEN1_2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=W_GENERAL1_L;
		end
		W_LADDR_1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=start_addr[15:8];
		end
		W_LADDR_2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=start_addr[7:0];
		end
		W_GEN2_1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=W_GENERAL2_H;
		end
		W_GEN2_2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=W_GENERAL2_L;
		end
		W_HADDR_1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=8'b0000_0011;		//Read command
		end
		W_HADDR_2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=start_addr[23:16];
		end
		W_CMD1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=W_CMD_H;
		end
		W_CMD2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=W_CMD_L;
		end
		CMD_REBOOT1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=CMD_REBOOT_H;
		end
		CMD_REBOOT2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=CMD_REBOOT_L;
		end
		NOP1:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=NOP_H;
		end
		NOP2:
		begin
			icap_ce<=1'b0;
			icap_write<=1'b0;
			icap_i<=NOP_L;
		end
		default:
		begin
			icap_ce<=1'b1;
			icap_write<=1'b1;
			icap_i<=8'hff;
		end
		endcase
	end
end

//refrence ug332.pdf
assign	icap_i_reverse={icap_i[0],icap_i[1],icap_i[2],icap_i[3],icap_i[4],icap_i[5],icap_i[6],icap_i[7]};

//ICAP instantiation
ICAP_SPARTAN3A ICAP_SPARTAN3A_inst (
      .BUSY(),   		// Busy output
      .O(),         		// 8-bit data output
      .CE(icap_ce),       	// Clock enable input
      .CLK(clk),     		// Clock input
      .I(icap_i_reverse),	// 8-bit data input
      .WRITE(icap_write)	// Write input
      );

assign	busy=(state==IDLE)?1'b0:1'b1;

endmodule
